use proc_macro::TokenStream;
use quote::quote;
use syn::punctuated::Punctuated;
use syn::token::Comma;
use syn::{Data, DeriveInput, Field, Fields, Ident, Type, parse_macro_input};

/// 检查是否为命名结构体，并返回字段
fn check_struct(ast: &Data) -> &Punctuated<Field, Comma> {
    match ast {
        Data::Struct(data_struct) => match &data_struct.fields {
            Fields::Named(fields) => &fields.named,
            Fields::Unnamed(_) => panic!("元组结构体不支持SetGetRole特型"),
            Fields::Unit => panic!("元组结构体不支持SetGetRole特型"),
        },
        _ => panic!("仅结构体支持SetGetRole派生宏"),
    }
}

/// 实现SetGetRole的派生宏<br/>
/// 作用于结构体，要求结构体必须为命名结构体，且有字段role，类型为crate::common::packages::Role
#[proc_macro_derive(SetGetRole)]
pub fn set_get_role_derive(input: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(input as DeriveInput);
    let name = &ast.ident;
    // 检查input是否为结构体
    let fields = check_struct(&ast.data);
    // 检查input的字段中是否有role
    let mut has_role = false;
    for f in fields.iter() {
        if f.ident.is_some() && f.ident.as_ref().unwrap() == "role" {
            if let Type::Path(type_path) = &f.ty {
                if type_path.path.segments.last()
                    .is_some_and(|t| t.ident == "Role") {
                    has_role = true;
                    break;
                }
            }
        }
    }
    if !has_role {
        panic!("结构体不包含role字段，无法实现SetGetRole")
    }
    // 生成代码
    quote! {
        impl SetGetRole for #name {
            fn set_role(&mut self, role: crate::common::packages::Role) {
                self.role = role;
            }
            fn get_role(&self) -> crate::common::packages::Role {
                self.role
            }
        }
    }
    .into()
}

/// 为PackageMetadata和自定义数据包实现GetAction特型<br/>
/// 对于PackageMetadata，必须包含字段pkg_action<br/>
/// 对于自定义数据包结构体，必须有命名规范`<Pkg><Dir>`，如ShellCmdReq
#[proc_macro_derive(GetAction)]
pub fn get_action_derive(input: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(input as DeriveInput);
    let name = &ast.ident;

    // 检查input是否为结构体
    let fields = check_struct(&ast.data);
    // 检查input的字段中是否有role
    let mut has_pkg_action = false;
    for f in fields.iter() {
        if f.ident.is_some() && f.ident.as_ref().unwrap() == "pkg_action" {
            if let Type::Path(type_path) =  &f.ty {
                if type_path.path.segments.last()
                    .is_some_and(|t| t.ident == "PackageAction") {
                    has_pkg_action = true;
                    break;
                }
            }
        }
    }
    fn gen_output(action: &Ident, dir: &Ident, name: &Ident) -> TokenStream {
        quote! {
            impl GetAction for #name {
                fn get_action(&self) -> crate::common::packages::PackageAction {
                    crate::common::packages::PackageAction::#action(crate::common::packages::TransmissionDirection::#dir)
                }
            }
        }.into()
    }
    // 如果没有pkg_action字段，则判断是否为数据包结构体
    if !has_pkg_action {
        let struct_name = &ast.ident.to_string();
        if struct_name.ends_with("Req") {
            let pkg_name = struct_name.trim_end_matches("Req");
            let s1 = Ident::new(pkg_name, proc_macro::Span::call_site().into());
            let s2 = Ident::new("Request", proc_macro::Span::call_site().into());
            gen_output(&s1, &s2, name)
        } else if struct_name.ends_with("Resp") {
            let pkg_name = struct_name.trim_end_matches("Resp");
            let s1 = Ident::new(pkg_name, proc_macro::Span::call_site().into());
            let s2 = Ident::new("Response", proc_macro::Span::call_site().into());
            gen_output(&s1, &s2, name)
        } else {
            panic!("结构体类型错误")
        }
    } else {
        quote! {
            impl GetAction for #name {
                fn get_action(&self) -> crate::common::packages::PackageAction {
                    self.pkg_action
                }
            }
        }
        .into()
    }
}
